import java.util.*;
import java.util.concurrent.*;

public class EpidemicNode extends Node
{
    Random gen = new Random();
    EpidemicData eParams;
    ArrayList<msgCounter> msgBuffer;
    
    public EpidemicNode(int ID, EpidemicData dat)
    {
        super(ID);
        eParams = dat;
        msgBuffer = new ArrayList<msgCounter>();
    }
    
    public EpidemicNode(int ID, Node node, EpidemicData dat)
    {
        super(ID,node);
        eParams = dat;
        msgBuffer = new ArrayList<msgCounter>();
    }
    
    @Override
    public void send(String message)
    {
        Random gen = new Random();
        int i,j;
        int fan = eParams.fanOut;
        
        // pick f random neighbors
        LinkedBlockingQueue[] toSend = new LinkedBlockingQueue[fan];
        Integer[] keys = new Integer[fan];
        
        for(i = 0; i < Math.min(fan,commChannels.size()); i++)
        {
            //choose key
            int index = gen.nextInt(commChannels.size());
            keys[i] = (Integer)commChannels.keySet().toArray()[index];
            toSend[i] = commChannels.get(keys[i]);
            
            // chose the same one... need to back up
            for(j = 0; j < i; j++)
                if(toSend[i] == toSend[j])
                    i--;
        }
        
        //System.out.print(nodeId + " send " + message + " -> ");
        
        // send the message to each
        for(i = 0; i < toSend.length; i++)
        {
            // could be null if less links than
            // fan out size
            if(toSend[i] != null)
                toSend[i].offer(message);
            
            //System.out.print(keys[i] + "  ");
        }
        //System.out.println();
        
        // increment count in the buffer
        for(j = 0; j < msgBuffer.size(); j++)
            if(msgBuffer.get(j).equals(message))
            {
                ++msgBuffer.get(j).count;
                /* System.out.println(nodeId + " [" +j + "].count = " + // 
                    msgBuffer.get(j).count); */ // debug
                // if count is the required amount, remove from buffer
                if(msgBuffer.get(j).count == eParams.repeat)
                    msgBuffer.remove(j);
                
                break; // for j
            }
    }
    
    @Override
    public void processMessage(String message)
    {
        if(information.contains(message))
        {
            copyCount++;
            return;
        }
        
        // if no room, drop or kick from buffer
        // this version drops (JCW)
        /*
        if(msgBuffer.size() == eParams.bufSize)
            return;
        */
        // this version kicks (JCW)
        
        // assumes that max buffer size is at least one,
        // pretty safe assumption...
        if(msgBuffer.size() == eParams.bufSize)
        {
            int max = 0;
            for(int i = 1; i < msgBuffer.size(); i++)
                if(msgBuffer.get(i).count > msgBuffer.get(max).count)
                    max = i;
            msgBuffer.remove(max);
        }
        
        
        // add to known information
        information.add(message);
        
        // add to buffer
        msgBuffer.add(new msgCounter(message));
        
        // initiate first send
        //send(message);
    }
    
    @Override
    public void step()
    {
        // don't need to do anything
        if(msgBuffer.size() == 0)
            return;
        
        // choose a message from the buffer and send it
        //    or
        // send all messages in the buffer
        
        // choose a single message
        /*
        Random gen = new Random();
        String message = msgBuffer.get(gen.nextInt(msgBuffer.size())).msg;
        send(message);
        */
        
        // send all messages
        
        // countdown because of possible deletion
        for(int i = msgBuffer.size()-1; i >= 0; i--)
            send(msgBuffer.get(i).msg);
    }
    
    /** counter to remember how many times a message has been sent */
    private class msgCounter
    {
        int count;
        String msg;
        
        public msgCounter(String message)
        {
            count = 0;
            msg = message;
        }
        
        // not sure if this is technically correct, 
        // but is OK here (JCW)
        public boolean equals(Object o)
        {
            String s;
            
            if(o instanceof msgCounter)
            {
                return msg.equals( ((msgCounter)o).msg );
            }
            if(o instanceof String)
                return msg.equals(o);
            
            return false;
        }
    }
}
